package com.cy.pj.sys.service.realm;

import com.cy.pj.sys.dao.SysMenuDao;
import com.cy.pj.sys.dao.SysRoleMenuDao;
import com.cy.pj.sys.dao.SysUserDao;
import com.cy.pj.sys.dao.SysUserRoleDao;
import com.cy.pj.sys.entity.SysUser;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 定义Realm类型继承AuthorizingRealm类型，
 * 假如只做认证可以直接继承认证AuthenticatingRealm即可
 */
@Service
public class ShiroUserRealm extends AuthorizingRealm {
    @Autowired
    private SysUserDao sysUserDao;

    @Autowired
    private SysUserRoleDao sysUserRoleDao;

    @Autowired
    private SysRoleMenuDao sysRoleMenuDao;

    @Autowired
    private SysMenuDao sysMenuDao;

    /**
     * 此方法用于获取用户的权限信息，并进行封装
     *
     * 例如去景点玩，买了票有的地方可以玩，有的地方需要另外掏钱才能玩
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SysUser user = (SysUser)principals.getPrimaryPrincipal();
        Integer userId = user.getId();
        List<Integer> roleIds = sysUserRoleDao.findRoleIdsByUserId(userId);
        if(roleIds == null || roleIds.size() == 0)
            throw new AuthorizationException();
        List<Integer> menuIds = sysRoleMenuDao.findMenuIdsByRoleIds(roleIds);
        if(menuIds == null || menuIds.size() == 0)
            throw new AuthorizationException();
        List<String> permissions = sysMenuDao.findPermissions(menuIds);
        Set<String> set = new HashSet<>();
        for(String s : permissions) {
            if(!ObjectUtils.isEmpty(s)) set.add(s);
        }
        System.out.println("ShiroUserRealm.doGetAuthorizationInfo 用户权限：" + set);
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setStringPermissions(set);
        return info;
    }

    /**
     * 此方法用于获取用户认证信息，并进行封装
     * @param token SecurityManager传过来的
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // 1、获取登录时输入的用户名
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        String username = upToken.getUsername();
        // 2、基于用户名查找数据库中的用户信息
        SysUser user = sysUserDao.findUserByUserName(username);
        // 3、校验用户是否存在
        if(user == null)
            throw new UnknownAccountException();
        // 4、校验用户是否已被禁用
        if(user.getValid() == 0)
            throw new LockedAccountException();
        // 5、封装用户信息并返回，将信息交给SecurityManager进行认证
        ByteSource credentialsSalt = ByteSource.Util.bytes(user.getSalt());
        SimpleAuthenticationInfo info =
                new SimpleAuthenticationInfo(user,
                        user.getPassword(),
                        credentialsSalt,
                        getName());
        return info;
    }

    @Override
    public CredentialsMatcher getCredentialsMatcher() {
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher("MD5");
        credentialsMatcher.setHashIterations(1);
        return credentialsMatcher;
    }
}
